﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using Milan.Windows;

namespace Milan.Install
{
	#region ShellLink Object
	/// <summary>
	/// Summary description for ShellLink.
	/// </summary>
	public class ShellLink : IDisposable
	{
		#region ShellLinkCoClass
		[GuidAttribute("00021401-0000-0000-C000-000000000046")]
		[ClassInterfaceAttribute(ClassInterfaceType.None)]
		[ComImportAttribute()]
		private class CShellLink { }
		#endregion

		#region Member Variables
		// Use Unicode (W) under NT, otherwise use ANSI      
		private IShellLinkW m_linkW;
		private string shortcutFile = "";
		#endregion

		#region Constructor
		/// <summary>
		/// Creates an instance of the Shell Link object.
		/// </summary>
		public ShellLink()
		{
			m_linkW = (IShellLinkW)new CShellLink();
		}

		/// <summary>
		/// Creates an instance of a Shell Link object
		/// from the specified link file
		/// </summary>
		/// <param name="linkFile">The Shortcut file to open</param>
		public ShellLink(string linkFile) : this()
		{
			Open(linkFile);
		}
		#endregion

		#region Destructor and Dispose
		/// <summary>
		/// Call dispose just in case it hasn't happened yet
		/// </summary>
		~ShellLink()
		{
			Dispose();
		}

		/// <summary>
		/// Dispose the object, releasing the COM ShellLink object
		/// </summary>
		public void Dispose()
		{
			if (m_linkW != null ) 
			{
				Marshal.ReleaseComObject(m_linkW);
				m_linkW = null;
			}
		}
		#endregion

		#region Implementation
		public string ShortCutFile
		{
			get
			{
				return this.shortcutFile;
			}
			set
			{
				this.shortcutFile = value;
			}
		}

		/// <summary>
		/// Gets a System.Drawing.Icon containing the icon for this
		/// ShellLink object.
		/// </summary>
		public Icon LargeIcon
		{
			get
			{
				return getIcon(true);
			}
		}

		public Icon SmallIcon
		{
			get
			{
				return getIcon(false);
			}
		}

		private Icon getIcon(bool large)
		{
			// Get icon index and path:
			int iconIndex = 0;
			StringBuilder iconPath = new StringBuilder(260, 260);
			m_linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
			string iconFile = iconPath.ToString();

			// If there are no details set for the icon, then we must use
			// the shell to get the icon for the target:
			if (iconFile.Length == 0)
			{
				// Use the FileIcon object to get the icon:
				shell32.SHGetFileInfoConstants flags = shell32.SHGetFileInfoConstants.SHGFI_ICON | shell32.SHGetFileInfoConstants.SHGFI_ATTRIBUTES;
				if (large)
				{
					flags = flags | shell32.SHGetFileInfoConstants.SHGFI_LARGEICON;
				}
				else
				{
					flags = flags | shell32.SHGetFileInfoConstants.SHGFI_SMALLICON;
				}
				FileIcon fileIcon = new FileIcon(Target, flags);
				return fileIcon.ShellIcon;
			}
			else
			{
				// Use ExtractIconEx to get the icon:
				IntPtr[] hIconEx = new IntPtr[1] {IntPtr.Zero};         
				int iconCount = 0;
				if (large)
				{
					iconCount = shell32.ExtractIconEx( iconFile, iconIndex, hIconEx, null, 1);
				}
				else
				{
					iconCount = shell32.ExtractIconEx( iconFile, iconIndex, null, hIconEx, 1);
				}
				// If success then return as a GDI+ object
				Icon icon = null;
				if (hIconEx[0] != IntPtr.Zero)
				{
					icon = Icon.FromHandle(hIconEx[0]);
					//UnManagedMethods.DestroyIcon(hIconEx[0]);
				}
				return icon;
			}            
		}

		/// <summary>
		/// Gets the path to the file containing the icon for this shortcut.
		/// </summary>
		public string IconPath
		{
			get
			{
				StringBuilder iconPath = new StringBuilder(260, 260);
				int iconIndex = 0;
				m_linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
				return iconPath.ToString();
			}
			set
			{
				StringBuilder iconPath = new StringBuilder(260, 260);
				int iconIndex = 0;
				m_linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
				m_linkW.SetIconLocation(value, iconIndex);
			}
		}

		/// <summary>
		/// Gets the index of this icon within the icon path's resources
		/// </summary>
		public int IconIndex
		{
			get
			{
				StringBuilder iconPath = new StringBuilder(260, 260);
				int iconIndex = 0;
				m_linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
				return iconIndex;
			}
			set
			{
				StringBuilder iconPath = new StringBuilder(260, 260);
				int iconIndex = 0;
				m_linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
				m_linkW.SetIconLocation(iconPath.ToString(), value);
			}
		}

		/// <summary>
		/// Gets/sets the fully qualified path to the link's target
		/// </summary>
		public string Target
		{
			get
			{      
				StringBuilder target = new StringBuilder(260, 260);
				kernel32.WIN32_FIND_DATA fd = new kernel32.WIN32_FIND_DATA();
				m_linkW.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY);
				return target.ToString();
			}
			set
			{
				m_linkW.SetPath(value);
			}
		}

		/// <summary>
		/// Gets/sets the Working Directory for the Link
		/// </summary>
		public string WorkingDirectory
		{
			get
			{
				StringBuilder path = new StringBuilder(260, 260);
				m_linkW.GetWorkingDirectory(path, path.Capacity);
				return path.ToString();
			}
			set
			{
				m_linkW.SetWorkingDirectory(value);   
			}
		}

		/// <summary>
		/// Gets/sets the description of the link
		/// </summary>
		public string Description
		{
			get
			{
				StringBuilder description = new StringBuilder(1024, 1024);
				m_linkW.GetDescription(description, description.Capacity);
				return description.ToString();
			}
			set
			{
				m_linkW.SetDescription(value);
			}
		}

		/// <summary>
		/// Gets/sets any command line arguments associated with the link
		/// </summary>
		public string Arguments
		{
			get
			{
				StringBuilder arguments = new StringBuilder(260, 260);
				m_linkW.GetArguments(arguments, arguments.Capacity);
				return arguments.ToString();
			}
			set
			{
				m_linkW.SetArguments(value);
			}
		}

		/// <summary>
		/// Gets/sets the initial display mode when the shortcut is
		/// run
		/// </summary>
		public LinkDisplayMode DisplayMode
		{
			get
			{
				uint cmd = 0;
				m_linkW.GetShowCmd(out cmd);
				return (LinkDisplayMode)cmd;
			}
			set
			{
				m_linkW.SetShowCmd((uint)value);
			}
		}

		/// <summary>
		/// Gets/sets the HotKey to start the shortcut (if any).  Use the Keys enumeration from System.Windows.Forms
		/// </summary>
		public ConsoleKey HotKey
		{
			get
			{
				short key = 0;
				m_linkW.GetHotkey(out key);
				return (ConsoleKey)key;
			}
			set
			{
				m_linkW.SetHotkey((short)value);
			}
		}

		/// <summary>
		/// Saves the shortcut to ShortCutFile.
		/// </summary>
		public void Save()
		{
			Save(shortcutFile);
		}

		/// <summary>
		/// Saves the shortcut to the specified file
		/// </summary>
		/// <param name="linkFile">The shortcut file (.lnk)</param>
		public void Save(string linkFile )
		{   
			// Save the object to disk
			((IPersistFile)m_linkW).Save(linkFile, true);
			shortcutFile = linkFile;
		}

		/// <summary>
		/// Loads a shortcut from the specified file
		/// </summary>
		/// <param name="linkFile">The shortcut file (.lnk) to load</param>
		public void Open( string linkFile   )
		{
			Open(linkFile, IntPtr.Zero, (EShellLinkResolveFlags.SLR_ANY_MATCH | EShellLinkResolveFlags.SLR_NO_UI), 1);
		}
      
		/// <summary>
		/// Loads a shortcut from the specified file, and allows flags controlling
		/// the UI behavior if the shortcut's target isn't found to be set.
		/// </summary>
		/// <param name="linkFile">The shortcut file (.lnk) to load</param>
		/// <param name="hWnd">The window handle of the application's UI, if any</param>
		/// <param name="resolveFlags">Flags controlling resolution behaviour</param>
		public void Open( string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags )
		{
			Open(linkFile, hWnd, resolveFlags, 1);
		}

		/// <summary>
		/// Loads a shortcut from the specified file, and allows flags controlling
		/// the UI behavior if the shortcut's target isn't found to be set.  If
		/// no SLR_NO_UI is specified, you can also specify a timeout.
		/// </summary>
		/// <param name="linkFile">The shortcut file (.lnk) to load</param>
		/// <param name="hWnd">The window handle of the application's UI, if any</param>
		/// <param name="resolveFlags">Flags controlling resolution behaviour</param>
		/// <param name="timeOut">Timeout if SLR_NO_UI is specified, in ms.</param>
		public void Open(string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags, ushort timeOut)
		{
			uint flags;

			if ((resolveFlags & EShellLinkResolveFlags.SLR_NO_UI) == EShellLinkResolveFlags.SLR_NO_UI)
			{
				flags = (uint)((int)resolveFlags | (timeOut << 16));
			}
			else
			{
				flags = (uint)resolveFlags;
			}

			((IPersistFile)m_linkW).Load(linkFile, 0); //STGM_DIRECT)
			m_linkW.Resolve(hWnd, flags);
			this.shortcutFile = linkFile;
		}
		#endregion
	}
	#endregion
}